08 实践课-LangChain 基础智能体原型开发
LangChain 基础智能体原型开发
关联:索引
- 场景提问:分拣员说“查一下玻璃制品怎么分拣”,系统怎么做到“查规则→返回标准→让结果可复验”?
sorting_agent_practice/
app.py
tools_sorting.py # 两个工具都写在这个文件里(规则查询/反馈回执)
intent.py
prompts.py
requirements.txt
2. 阶段式实现路线(从零到最小闭环综合项目)
最终交付目标:在 sorting_agent_practice/ 下运行 python app.py,完成“接收指令 → 意图解析 → 触发工具/Agent → 输出可复验依据”的最小闭环。
- 阶段 0(环境与 Key):创建 Conda 环境 + 安装固定版本依赖 + 配置
.env - 阶段 1(执行层):实现 2 个 Tool(规则查询 / 反馈回执),确保输出可复验
- 阶段 2(约束层):实现 Prompt 模板(强约束:必须引用规则编号/回执/trace_id)
- 阶段 3(Agent 最小闭环):初始化 LLM + 注册 Tool + 用
create_agent组装可调用的 Agent,先跑通一次工具调用 - 阶段 4(感知/决策层):实现企业级“结构化意图识别”(规则优先 + LLM JSON 回退 + 失败兜底)
python==3.10.12
langchain==1.2.7
langchain-core==1.2.17
langchain-community==0.4.1
langgraph==1.0.10(本节不强制使用,可作为进阶编排)
dashscope==1.25.13
python-dotenv==1.2.2
fastapi[all]==0.111.0(本节不强制使用;用于后续 API 服务化;安装后通常会带上 pydantic)
uvicorn==0.23.2(本节不强制使用;用于后续 API 运行)
安装(示例,Conda + Pip,推荐):
conda create -n sorting-agent python=3.10.12 -y
conda activate sorting-agent
pip install langchain==1.2.7 langchain-core==1.2.17 langchain-community==0.4.1 langgraph==1.0.10 dashscope==1.25.13 python-dotenv==1.2.2 "fastapi[all]==0.111.0" uvicorn==0.23.2
如果你已经创建并激活了 Conda 环境:直接执行 pip install ... 这一行即可。
说明:
- 本“企业级结构化意图识别”示例使用了 Pydantic(v1/v2 均可);安装了
fastapi[all]==0.111.0一般会同时安装 Pydantic。
4. 通义千问 Key 配置(演示规范)
- 建议用环境变量或
.env文件,不要把 Key 写进代码或提交仓库。 - 如果你用
.env:把.env放在项目根目录(与app.py同级),并确保不提交到仓库。
.env 示例:
DASHSCOPE_API_KEY=你的_key
Python 里读取 .env 的关键点:在初始化 LLM 之前调用 load_dotenv(),让环境变量进入 os.getenv()。
如果你是从项目外层目录运行脚本,导致 .env 没被找到,优先检查当前工作目录是否为项目根目录。
- 示例(bash):
export DASHSCOPE_API_KEY="你的_key"
- 示例(Windows PowerShell):
$env:DASHSCOPE_API_KEY="你的_key"
setx DASHSCOPE_API_KEY "你的_key"
- 输入要可校验:不要让工具吃“任意自然语言”,尽量结构化(如 item_type、scene)。
- 输出要可复验:返回“规则编号/规则文本/依据来源/建议动作”,避免只回一句话。
- 异常要可解释:输入不合法要返回明确错误,不要“默默失败”。
2. 阶段 1:示例工具 1(规则查询,最小可用)
文件:tools_sorting.py(关键结构示例)
from langchain_core.tools import tool
import uuid
_RULES = {
"glass": {
"rule_id": "R-GLASS-01",
"rule_text": "玻璃制品:轻放、防碰撞;单独箱;贴易碎标;优先人工复核。",
"action": "use_fragile_bin",
},
"battery": {
"rule_id": "R-BATT-02",
"rule_text": "电池类:绝缘包装;远离金属;单独分拣;异常鼓包立即隔离。",
"action": "use_battery_bin",
},
}
@tool("query_sorting_rule")
def query_sorting_rule(item_type: str) -> str:
"""查询分拣规则。输入为物品类型英文关键字,如 glass/battery。返回规则摘要。"""
trace_id = uuid.uuid4().hex[:8]
k = (item_type or "").strip().lower()
if not k:
return f"ERROR: item_type is empty | trace_id={trace_id}"
rule = _RULES.get(k)
if not rule:
return f"NOT_FOUND: no rule for item_type={k} | trace_id={trace_id}"
return f"{rule['rule_id']} | {rule['rule_text']} | action={rule['action']} | trace_id={trace_id}"
3. 阶段 1:示例工具 2(结果反馈,用于记录异常/结果)
文件:tools_sorting.py(同一文件,继续追加第二个工具)
from langchain_core.tools import tool
import uuid
@tool("submit_sorting_feedback")
def submit_sorting_feedback(task_id: str, ok: bool, message: str) -> str:
"""提交分拣反馈(演示版:返回可验收的回执字符串)。"""
trace_id = uuid.uuid4().hex[:8]
task_id = (task_id or "").strip()
if not task_id:
return f"ERROR: task_id is empty | trace_id={trace_id}"
msg = (message or "").strip()
if not msg:
return f"ERROR: message is empty | trace_id={trace_id}"
status = "OK" if ok else "FAIL"
return f"RECEIPT: task_id={task_id} status={status} message={msg} | trace_id={trace_id}"
- System:角色与安全边界(必须优先于用户输入)。
- Human:用户指令原文。
- Tool Result:工具返回必须被引用(可复验的“依据”)。
- Agent 调用:本阶段 3 使用
create_agent(..., system_prompt=...),以 message 形式invoke;如采用ChatPromptTemplate组装工具调用链,才需要agent_scratchpad之类的占位符承载中间过程。
文件:prompts.py(示例)
SYSTEM_PROMPT_TEXT = (
"你是分拣车间的智能助手。优先使用工具获取规则或生成回执。回答必须包含可复验依据(如规则编号/回执/trace_id)。"
"不要输出或猜测任何密钥。遇到信息不足先追问澄清,不要编造规则。"
)
2. LangChain Agent 最小闭环(示例)
文件:app.py(示例结构)
阶段 3 目标:先不做路由与意图识别,仅跑通 “LLM + Tool + Prompt + Agent(create_agent)” 的最小闭环。
import os
from dotenv import load_dotenv
from langchain_core.messages import HumanMessage
try:
from langchain_community.chat_models import ChatTongyi
except ImportError:
from langchain_community.chat_models.tongyi import ChatTongyi
from prompts import SYSTEM_PROMPT_TEXT
from tools_sorting import query_sorting_rule, submit_sorting_feedback
load_dotenv()
def build_llm():
if not os.getenv("DASHSCOPE_API_KEY"):
raise RuntimeError("DASHSCOPE_API_KEY is not set")
return ChatTongyi(model="qwen-plus", temperature=0)
def main():
llm = build_llm()
tools = [query_sorting_rule, submit_sorting_feedback]
user_input = "查一下 glass 的分拣规则"
from langchain.agents import create_agent
agent = create_agent(model=llm, tools=tools, system_prompt=SYSTEM_PROMPT_TEXT, debug=True)
out = agent.invoke({"messages": [HumanMessage(content=user_input)]})
messages = out.get("messages") or []
final_message = messages[-1] if messages else None
print(getattr(final_message, "content", out))
if __name__ == "__main__":
main()
- 快问快答:为什么要写意图识别?(答案要包含:稳定性、可控性、可测试性)
QUERY_RULE:查询分拣规则(包含物品类型)SUBMIT_FEEDBACK:提交分拣结果/异常反馈(包含 task_id、ok、message)GENERAL:普通问答/解释类(不触发工具或由模型决定)
2. 指令解析与意图匹配(规则优先、LLM 回退)
企业级主流实现(本采用,替换正则示例):
- 核心思路:规则优先(强确定性/强合规)+ LLM 结构化回退(承接自然语言长尾表达)。
- 输出要求:意图 + 槽位字段必须结构化(JSON),并带
confidence与need_clarification,便于审计与可控回退。
文件:intent.py(示例)
import re
import json
import uuid
from dataclasses import dataclass
from typing import Literal, Optional
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field, ValidationError
class IntentPayload(BaseModel):
intent: Literal["QUERY_RULE", "SUBMIT_FEEDBACK", "GENERAL"]
item_type: Optional[str] = None
task_id: Optional[str] = None
ok: Optional[bool] = None
message: Optional[str] = None
confidence: float = Field(default=0.0, ge=0.0, le=1.0)
need_clarification: bool = False
clarification_question: Optional[str] = None
@dataclass
class IntentResult:
trace_id: str
intent: str
item_type: Optional[str] = None
task_id: Optional[str] = None
ok: Optional[bool] = None
message: Optional[str] = None
confidence: float = 0.0
need_clarification: bool = False
clarification_question: Optional[str] = None
no_tools: bool = False
def detect_intent(text: str, llm) -> IntentResult:
trace_id = uuid.uuid4().hex[:8]
t = (text or "").strip()
if not t:
return IntentResult(
trace_id=trace_id,
intent="GENERAL",
confidence=0.0,
need_clarification=True,
clarification_question="你想查询分拣规则,还是提交分拣反馈?请补充关键信息。",
)
explanation_keywords = (
"为什么",
"为何",
"原理",
"原因",
"区别",
"差别",
"对比",
"比较",
)
if any(k in t for k in explanation_keywords):
return IntentResult(trace_id=trace_id, intent="GENERAL", confidence=1.0, no_tools=True)
tokens = t.split()
if len(tokens) >= 2 and tokens[0].upper() == "RULE":
item_type = tokens[1].strip().lower()
if not item_type:
return IntentResult(
trace_id=trace_id,
intent="QUERY_RULE",
confidence=1.0,
need_clarification=True,
clarification_question="请提供物品类型关键字(如 glass/battery)。",
)
return IntentResult(trace_id=trace_id, intent="QUERY_RULE", item_type=item_type, confidence=1.0)
if len(tokens) >= 4 and tokens[0].upper() == "FEEDBACK":
ok = tokens[2].upper() in {"OK", "SUCCESS", "TRUE"}
task_id = tokens[1].strip()
message = " ".join(tokens[3:]).strip()
if not task_id or not message:
return IntentResult(
trace_id=trace_id,
intent="SUBMIT_FEEDBACK",
confidence=1.0,
need_clarification=True,
clarification_question="请补充 task_id 与反馈内容(例如:FEEDBACK t001 FAIL 玻璃破损)。",
)
return IntentResult(trace_id=trace_id, intent="SUBMIT_FEEDBACK", task_id=task_id, ok=ok, message=message, confidence=1.0)
if ("规则" in t or "分拣规则" in t or "怎么分拣" in t or "如何分拣" in t or "怎么处理" in t):
m = re.search(r"[A-Za-z][A-Za-z0-9_-]*", t)
if not m:
return IntentResult(
trace_id=trace_id,
intent="QUERY_RULE",
confidence=0.9,
need_clarification=True,
clarification_question="你要查询哪一类物品的规则?请给出关键字(如 glass/battery)。",
)
return IntentResult(
trace_id=trace_id,
intent="QUERY_RULE",
item_type=m.group(0).lower(),
confidence=0.9,
)
if t.startswith("提交反馈") or t.startswith("反馈") or "提交反馈" in t or "反馈" in t:
task_id_match = re.search(r"\b[tT]\d+\b", t)
task_id = task_id_match.group(0) if task_id_match else None
ok: Optional[bool]
if any(x in t for x in ["失败", "异常", "破损", "报错", "错"]):
ok = False
elif any(x in t for x in ["成功", "正常", "OK", "ok"]):
ok = True
else:
ok = None
msg = t
msg = msg.replace("提交反馈", "").replace("反馈", "").strip()
if task_id:
msg = msg.replace(task_id, "").strip()
msg = msg.strip(" ,,::")
if not task_id:
return IntentResult(
trace_id=trace_id,
intent="SUBMIT_FEEDBACK",
confidence=0.9,
need_clarification=True,
clarification_question="请补充 task_id(例如:提交反馈 t001 失败,玻璃有破损)。",
)
if ok is None:
return IntentResult(
trace_id=trace_id,
intent="SUBMIT_FEEDBACK",
task_id=task_id,
confidence=0.9,
need_clarification=True,
clarification_question="本次分拣结果是成功还是失败?请补充“成功/失败”。",
)
if not msg:
return IntentResult(
trace_id=trace_id,
intent="SUBMIT_FEEDBACK",
task_id=task_id,
ok=ok,
confidence=0.9,
need_clarification=True,
clarification_question="请补充反馈内容(例如:玻璃破损/电池鼓包)。",
)
return IntentResult(
trace_id=trace_id,
intent="SUBMIT_FEEDBACK",
task_id=task_id,
ok=ok,
message=msg,
confidence=0.9,
)
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"你是企业系统里的指令解析器。把用户输入解析成严格 JSON(只输出 JSON,不要多余文字)。\n"
"JSON 字段:intent(QUERY_RULE/SUBMIT_FEEDBACK/GENERAL), item_type, task_id, ok, message, confidence(0-1), need_clarification(true/false), clarification_question。\n"
"规则:\n"
"1) 缺少关键槽位时,need_clarification=true,并给出 clarification_question。\n"
"2) 不确定时 intent=GENERAL,confidence 低。\n"
"3) 不要编造 task_id 或 item_type。",
),
("human", "{text}"),
]
)
raw = llm.invoke(prompt.format_messages(text=t)).content
start = raw.find("{")
end = raw.rfind("}")
if start < 0 or end < 0 or end <= start:
return IntentResult(trace_id=trace_id, intent="GENERAL", confidence=0.0)
try:
data = json.loads(raw[start : end + 1])
if hasattr(IntentPayload, "model_validate"):
payload = IntentPayload.model_validate(data)
else:
payload = IntentPayload.parse_obj(data)
except (json.JSONDecodeError, ValidationError):
return IntentResult(trace_id=trace_id, intent="GENERAL", confidence=0.0)
return IntentResult(
trace_id=trace_id,
intent=payload.intent,
item_type=payload.item_type,
task_id=payload.task_id,
ok=payload.ok,
message=payload.message,
confidence=payload.confidence,
need_clarification=payload.need_clarification,
clarification_question=payload.clarification_question,
)
- 每个意图至少 1 条正例 + 1 条反例(不只测“命中”,还要测“槽位是否齐/是否需要澄清”)
- 解析失败必须回落到
GENERAL,不要抛异常导致系统崩溃
企业级补充要求(内已在代码示例体现):
- 槽位抽取:模型回退必须输出结构化字段(JSON),避免靠自然语言猜参数。
- 可靠性:模型输出无法解析/置信度过低时回退到
GENERAL,并触发澄清问题。 - 可观测性:
trace_id在意图解析、路由、工具回执中贯穿,形成完整证据链。
1. 路由统一口径
QUERY_RULE:直接调用query_sorting_rule工具,工具结果进入统一 Prompt 输出。SUBMIT_FEEDBACK:直接调用submit_sorting_feedback工具,输出回执。GENERAL:交给 Agent(或仅用 LLM + Prompt)生成解释性回答,但必须声明“未查询到规则/未调用工具”的原因(可审计)。
2. 路由实现示例(把意图模块接进 app.py)
- 当
need_clarification=true时先追问补齐槽位,再进入工具调用,减少“猜参数”导致的误调用。
from intent import detect_intent
from langchain_core.messages import HumanMessage, SystemMessage
from prompts import SYSTEM_PROMPT_TEXT
from tools_sorting import query_sorting_rule, submit_sorting_feedback
def run_with_router(agent, llm, user_input: str) -> str:
ir = detect_intent(user_input, llm)
if ir.need_clarification:
q = ir.clarification_question or "请补充关键信息后再试一次。"
return f"【需要澄清】{q} | parse_trace_id={ir.trace_id}"
if ir.intent == "QUERY_RULE":
tool_out = query_sorting_rule.invoke({"item_type": ir.item_type})
return f"【规则查询结果】{tool_out} | parse_trace_id={ir.trace_id}"
if ir.intent == "SUBMIT_FEEDBACK":
tool_out = submit_sorting_feedback.invoke(
{"task_id": ir.task_id, "ok": ir.ok, "message": ir.message}
)
return f"【反馈回执】{tool_out} | parse_trace_id={ir.trace_id}"
if ir.intent == "GENERAL" and getattr(ir, "no_tools", False):
msg = llm.invoke([SystemMessage(content=SYSTEM_PROMPT_TEXT), HumanMessage(content=user_input)])
content = getattr(msg, "content", str(msg))
return f"【解释】{content} | no_tools=true | parse_trace_id={ir.trace_id}"
out = agent.invoke({"messages": [HumanMessage(content=user_input)]})
messages = out.get("messages") or []
tool_called = any(getattr(m, "type", None) == "tool" for m in messages)
final_message = messages[-1] if messages else None
content = getattr(final_message, "content", str(out))
if tool_called:
return f"{content} | parse_trace_id={ir.trace_id}"
return f"{content} | no_tool_called=true | parse_trace_id={ir.trace_id}"
3. 阶段 5:综合项目版 app.py(最终最小闭环)
import os
from dotenv import load_dotenv
from langchain_core.messages import HumanMessage, SystemMessage
try:
from langchain_community.chat_models import ChatTongyi
except ImportError:
from langchain_community.chat_models.tongyi import ChatTongyi
from intent import detect_intent
from prompts import SYSTEM_PROMPT_TEXT
from tools_sorting import query_sorting_rule, submit_sorting_feedback
load_dotenv()
def build_llm():
if not os.getenv("DASHSCOPE_API_KEY"):
raise RuntimeError("DASHSCOPE_API_KEY is not set")
return ChatTongyi(model="qwen-plus", temperature=0)
def build_agent(llm):
tools = [query_sorting_rule, submit_sorting_feedback]
from langchain.agents import create_agent
return create_agent(model=llm, tools=tools, system_prompt=SYSTEM_PROMPT_TEXT, debug=True)
def run_with_router(agent, llm, user_input: str) -> str:
ir = detect_intent(user_input, llm)
if ir.need_clarification:
q = ir.clarification_question or "请补充关键信息后再试一次。"
return f"【需要澄清】{q} | parse_trace_id={ir.trace_id}"
if ir.intent == "QUERY_RULE":
tool_out = query_sorting_rule.invoke({"item_type": ir.item_type})
return f"【规则查询结果】{tool_out} | parse_trace_id={ir.trace_id}"
if ir.intent == "SUBMIT_FEEDBACK":
tool_out = submit_sorting_feedback.invoke(
{"task_id": ir.task_id, "ok": ir.ok, "message": ir.message}
)
return f"【反馈回执】{tool_out} | parse_trace_id={ir.trace_id}"
if ir.intent == "GENERAL" and getattr(ir, "no_tools", False):
msg = llm.invoke([SystemMessage(content=SYSTEM_PROMPT_TEXT), HumanMessage(content=user_input)])
content = getattr(msg, "content", str(msg))
return f"【解释】{content} | no_tools=true | parse_trace_id={ir.trace_id}"
out = agent.invoke({"messages": [HumanMessage(content=user_input)]})
messages = out.get("messages") or []
tool_called = any(getattr(m, "type", None) == "tool" for m in messages)
final_message = messages[-1] if messages else None
content = getattr(final_message, "content", str(out))
if tool_called:
return f"{content} | parse_trace_id={ir.trace_id}"
return f"{content} | no_tool_called=true | parse_trace_id={ir.trace_id}"
def main():
llm = build_llm()
agent = build_agent(llm)
test_inputs = [
"查一下 glass 的分拣规则",
"提交反馈 t001 失败,玻璃有破损",
"为什么电池要单独分拣?",
"",
]
for s in test_inputs:
print("USER:", s)
print("ASSISTANT:", run_with_router(agent, llm, s))
print("-" * 60)
if __name__ == "__main__":
main()
- 查询规则:
- 输入:
查一下 glass 的分拣规则 - 期望:
QUERY_RULE,调用query_sorting_rule,输出包含R-GLASS-01且有trace_id
- 提交反馈:
- 输入:
提交反馈 t001 失败,玻璃有破损 - 期望:
SUBMIT_FEEDBACK,调用submit_sorting_feedback,输出包含RECEIPT且有trace_id
- 普通咨询:
-
输入:
为什么电池要单独分拣? -
期望:
GENERAL,不强制调用工具(或由 Agent 决策),回答包含可解释理由与边界说明 -
看到了什么:本次输入被解析成什么意图(QUERY_RULE / SUBMIT_FEEDBACK / GENERAL),缺什么信息?
-
选了什么:本次选择了哪个工具(或为何不调用工具)?
-
做了什么:工具返回的关键证据是什么(rule_id/receipt/trace_id),你如何证明结果可复现?
1. Key / 权限问题
- 现象:运行时报
DASHSCOPE_API_KEY is not set或鉴权失败 - 排查顺序:
- 检查环境变量是否生效(新终端是否加载)
- 检查 Key 是否含空格/引号错误
2. 依赖与版本问题
- 现象:
ModuleNotFoundError: langchain_community...或create_agent不存在 - 排查顺序:
pip show langchain langchain-community langchain-core查看版本- 确认是否安装了
langchain-community - 统一在虚拟环境中安装,避免系统 Python 污染
3. 工具不触发 / 输出不可复验
- 现象:回答没有规则编号、没有回执,像“纯聊天”
- 排查顺序:
- 路由是否命中(打印/记录意图识别结果)
- 工具输入是否为空或不合法(工具返回 ERROR/NOT_FOUND)
- Prompt 是否要求“必须引用工具结果”(System 约束是否足够明确)
- AI 可以用来:生成目录结构、生成工具骨架、补齐 Prompt、优化意图规则、分析报错
- 必须提交:
- 你向 AI 提问的关键提示词
- AI 的关键输出(保留原文)
- 你的人工审计与修改点(至少 3 条)
- 你的测试证据(3 个用例输入与关键输出截图/粘贴)
提示词模板(可直接复制):
你是 LangChain 智能体开发专家。请基于“仓储分拣指令处理”场景,输出一个 最小可运行 的智能体原型项目(可在 Windows/conda 环境运行)。请严格按要求生成内容,避免无关扩展。
一、目标
- 产出一个可运行 Demo:用户输入一句话,系统先做意图识别(规则匹配),再决定是否调用工具;解释类问题走 GENERAL 直接解释,不触发工具。
- 使用 LangChain 新版 API : from langchain.agents import create_agent ,不得使用 AgentExecutor / create_tool_calling_agent 。
二、组件要求(必须全部满足)
1. 初始化通义千问 LLM
- 使用 langchain_community.chat_models.ChatTongyi
- Key 通过环境变量 DASHSCOPE_API_KEY 读取(可用 python-dotenv 加载 .env )
- 代码中严禁写死 API Key
2. 定义 2 个工具(Tool)
- query_sorting_rule(item_type: str) -> str :返回包含 rule_id 与 trace_id 的可复验字符串
- submit_sorting_feedback(task_id: str, ok: bool, message: str) -> str :返回包含回执字段与 trace_id 的可复验字符串
- Tool 输出必须包含可复验依据:规则编号或回执 + trace_id
3. 定义 system_prompt
- 约束:优先使用工具获取规则/生成回执;回答必须包含可复验依据;信息不足先追问澄清;不要输出或猜测任何密钥
- system_prompt 统一放在 prompts.py (例如 SYSTEM_PROMPT_TEXT ),应用端复用该变量
4. 用 create_agent 组装并调用
- Agent 用 system_prompt=SYSTEM_PROMPT_TEXT
- 演示调用: agent.invoke({"messages": [HumanMessage(content=user_input)]})
三、意图识别模块(必须给出可测试的规则匹配实现)
- 单独文件 intent.py ,提供 detect_intent(text: str, llm) -> IntentResult
- 意图枚举: QUERY_RULE / SUBMIT_FEEDBACK / GENERAL
- 规则优先级(从高到低):
1. 空输入:需要澄清(need_clarification=true,给 clarification_question)
2. 解释类问题:只要输入 以/包含 “为什么/原理/原因/区别/差别/对比/比较/为何”等,直接 GENERAL ,并 显式标记 no_tools=true (确保不会触发工具)
3. 显式规则查询:包含“规则/分拣规则/怎么分拣/如何分拣/怎么处理”等,提取 item_type(英文关键字优先),否则需要澄清
4. 反馈类:包含“提交反馈/反馈”等,抽取 task_id(如 t001),推断 ok(成功/失败),缺槽位则需要澄清
5. 其余情况:GENERAL(可选:允许用 LLM 输出 JSON 兜底,但必须严格 JSON、异常回退 GENERAL)
四、可测试用例(必须提供 3 条 + 期望结果) 请给出 3 条输入与期望的 intent 路由结果(写成表格或列表),至少包含:
- 规则查询例(应走 QUERY_RULE 并调用 query_sorting_rule)
- 反馈提交例(应走 SUBMIT_FEEDBACK 并调用 submit_sorting_feedback)
- 解释类例(例如“为什么电池要单独分拣?”应走 GENERAL 且 no_tools=true,不调用工具)
五、约束与错误处理(必须体现到代码与说明中)
- 工具输入输出可复验(规则编号/回执 + trace_id)
- 异常必须返回明确错误信息(例如未配置 Key、依赖缺失、网络失败、解析失败)
- 不得输出任何 API Key 或提示用户粘贴 Key 到代码
- 输出必须是可复制运行的完整代码(不要省略关键导入与依赖)
六、排错清单(必须给 4 个常见报错,格式固定) 按以下格式给出 4 个常见报错的排查:
- 现象 → 原因 → 定位 → 修复 → 复验
必须覆盖:
1. Key 未配置/读取不到
2. 网络/限流/服务不可用
3. 依赖缺失或版本不匹配(尤其 LangChain 新旧 API 导入错误)
4. 工具不触发(意图识别误判、解释类问题误走工具、路由逻辑错误)
七、输出格式(严格按顺序输出,不要夹杂多余解释)
1. 推荐目录结构(树形)
2. requirements.txt(完整内容)
3. 关键代码文件内容(逐个文件给出完整内容,至少包括):
- app.py (最小运行 demo:直接走 create_agent + tool)
- app_with_intent.py (带意图路由:QUERY_RULE/SUBMIT_FEEDBACK/GENERAL + no_tools 分支)
- tools_sorting.py
- intent.py
- prompts.py
4. 3 条测试用例与期望结果
5. 排错清单(4 条,“现象→原因→定位→修复→复验”)
八、综合项目(最终最小闭环,对照拼装)
tools_sorting.py:阶段 1(2 个 Tool,返回规则编号/回执 + trace_id)prompts.py:阶段 2(统一输出约束:system_prompt)intent.py:阶段 4(规则优先 + LLM JSON 回退 + 失败兜底)app.py:阶段 5(最终版:build_llm/build_agent/run_with_router/main)
运行方式(项目根目录下):
python app.py
- 输入“查一下 glass 的分拣规则”能稳定返回
R-GLASS-01等可复验证据 - 输入“提交反馈 t001 失败,玻璃有破损”能返回
RECEIPT回执 - 输入“为什么电池要单独分拣?”能输出解释性回答,并体现边界(是否调用工具由路由/Agent 决策)
作业:布置
1)提交智能体基础框架代码(含 LLM 配置、Tool 开发)
2)提交意图识别逻辑代码及测试截图(至少 3 个测试用例)
3)提交 AI 辅助生成 / 优化智能体代码的交互记录及优化后的代码